import { injectable } from "inversify";
import { Event, Uri, Disposable } from "vscode";
import { Resource } from "../common/types";
import { EQEnvironmentsChangedEvent, IComponentAdapter, ICondaService } from "../interpreter/contracts";
import { IServiceManager } from "../ioc/types";
import { EQEnvInfo, EQEnvKind, EQEnvSource } from "./base/info";
import { IDiscoveryAPI } from "./base/locator";
import { CondaEnvironmentInfo } from "./common/environmentManagers/conda";
import { EnvironmentType, EQEnvironment } from "./info";
import * as vscode from 'vscode';
import { EQVersion } from "./info/eqVersion";
import { toSemverLikeVersion } from "./base/info/eqVersion";
import { CondaService } from "./common/environmentManagers/condaService";

const convertedKinds = new Map(
    Object.entries({
        [EQEnvKind.OtherGlobal]: EnvironmentType.Global,
        [EQEnvKind.WindowsStore]: EnvironmentType.WindowsStore,
        [EQEnvKind.Conda]: EnvironmentType.Conda,
    }),
);

function convertEnvInfo(info: EQEnvInfo): EQEnvironment {
    const { name, location, executable, arch, kind, version, distro, id } = info;
    const { filename, sysPrefix } = executable;
    const env: EQEnvironment = {
        id,
        sysPrefix,
        envType: EnvironmentType.Unknown,
        envName: name,
        envPath: location,
        path: filename,
        architecture: arch,
    };

    const envType = convertedKinds.get(kind);
    if (envType !== undefined) {
        env.envType = envType;
    }
    // Otherwise it stays Unknown.

    if (version !== undefined) {
        const { release, sysVersion } = version;
        if (release === undefined) {
            env.sysVersion = '';
        } else {
            env.sysVersion = sysVersion;
        }

        const semverLikeVersion: EQVersion = toSemverLikeVersion(version);
        env.version = semverLikeVersion;
    }

    if (distro !== undefined && distro.org !== '') {
        env.companyDisplayName = distro.org;
    }
    env.displayName = info.display;
    env.detailedDisplayName = info.detailedDisplayName;
    // We do not worry about using distro.defaultDisplayName.

    return env;
}

@injectable()
class ComponentAdapter implements IComponentAdapter {
    private readonly changed = new vscode.EventEmitter<EQEnvironmentsChangedEvent>();
    constructor(
        // The adapter only wraps one thing: the component API.
        private readonly api: IDiscoveryAPI,
    ) {
        this.api.onChanged((event) => {
            this.changed.fire({
                type: event.type,
                new: event.new ? convertEnvInfo(event.new) : undefined,
                old: event.old ? convertEnvInfo(event.old) : undefined,
                resource: event.searchLocation,
            });
        });
    }


    public get onRefreshStart(): vscode.Event<void> {
        return this.api.onRefreshStart;
    }
    triggerRefresh(query?: { kinds?: EQEnvKind[] | undefined; } & { searchLocations?: { roots: Uri[]; doNotIncludeNonRooted?: boolean | undefined; } | undefined; } & { clearCache?: boolean | undefined; }, trigger?: "auto" | "ui"): Promise<void> {
        throw new Error("16Method not implemented.");
    }
    refreshPromise: Promise<void> | undefined;
    public get onChanged() {
        return this.changed.event;
    }
    onDidCreate(resource: Resource, callback: () => void): Disposable {
        throw new Error("17Method not implemented.");
    }
    hasInterpreters(filter?: (e: EQEnvironment) => Promise<boolean>): Promise<boolean> {
        throw new Error("18Method not implemented.");
    }
    getInterpreters(resource?: Uri, source?: EQEnvSource[]): EQEnvironment[] {
        throw new Error("19Method not implemented.");
    }
    getWorkspaceVirtualEnvInterpreters(resource: Uri, options?: { ignoreCache?: boolean | undefined; }): Promise<EQEnvironment[]> {
        throw new Error("20Method not implemented.");
    }
    getInterpreterDetails(eqPath: string): Promise<EQEnvironment | undefined> {
        throw new Error("21Method not implemented.");
    }
    getInterpreterInformation(eqPath: string): Promise<Partial<EQEnvironment> | undefined> {
        throw new Error("22Method not implemented.");
    }
    isMacDefaultEQPath(eqPath: string): Promise<boolean> {
        throw new Error("23Method not implemented.");
    }
    isCondaEnvironment(interpreterPath: string): Promise<boolean> {
        throw new Error("24Method not implemented.");
    }
    getCondaEnvironment(interpreterPath: string): Promise<CondaEnvironmentInfo | undefined> {
        throw new Error("25Method not implemented.");
    }
    isWindowsStoreInterpreter(eqPath: string): Promise<boolean> {
        throw new Error("26Method not implemented.");
    }

}
export function registerNewDiscoveryForIOC(serviceManager: IServiceManager, api: IDiscoveryAPI): void {
    serviceManager.addSingletonInstance<IComponentAdapter>(IComponentAdapter, new ComponentAdapter(api));
    serviceManager.addSingleton<ICondaService>(ICondaService, CondaService);
}